package uia.alumni.form;

import uia.alumni.form.exception.*;
import java.util.ArrayList;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; // Sessions are required for request.getParameter() I think.

import static uia.alumni.form.FieldType.*;

/**
 * Class used to create and validate forms.
 * It has some limitations regarding to field types and
 * parameter values, but the most important ones are covered.
 *
 * Current Limitations to forms is that you cant change the type
 * on the fly. Once a field is created it's bound to a type.
 * 
 * @author Simon Zimmermann
 * @version 1.1
 * @since 24.10.2009
 */
public class Form {

    protected HttpServletRequest request;
    private boolean isBound;
    private ArrayList<FormField> fieldsList;
    private ArrayList<FormField> errors;
    private FieldTypes fieldTypes;

    /**
     * Unbound form. Doesnt have a purpose at the moment.
     * The point is that this could be used to create a form without
     * writing the HTML. 
     */
    protected Form() {
        this.fieldTypes = new FieldTypes();
        this.fieldsList = new ArrayList<FormField>();
        this.errors = new ArrayList<FormField>();
        this.isBound = false;
    }

    /**
     * Bound state of a HttpServletRequest object. Currently
     * only meaningful way of creating a form.
     * @param request
     */
    public Form(HttpServletRequest request) {
        this.request = request;
        this.fieldTypes = new FieldTypes();
        this.fieldsList = new ArrayList<FormField>();
        this.errors = new ArrayList<FormField>();
        this.isBound = true;
    }

    /**
     * Checks to see if the Entire Form is valid.
     * If errors are found they are put in a errorlist which
     * is accessible and can be used for debugging. This helps with
     * automatically handling error's in forms. Less typing :)
     * 
     * @return true if no errors are found.
     */
    public boolean isValid() {
        boolean valid = true;
        errors.clear(); // Maybe take this away?
        for (FormField formField : getFieldsList()) {
            if (!formField.isFieldValid()) {
                valid = false;
                errors.add(formField);
            }
        }
        return valid;
    }

    /**
     * Creates a new field if the type is correct.
     * @param name
     * @param type enum type with a string
     * @throws FieldTypeNotFoundException
     */
    public FormField newField(String name, FieldType type)
            throws FieldTypeNotFoundException, FieldNotFoundException {
        if (fieldTypes.isFieldType(type)) {
            fieldsList.add(new FormField(name, fieldTypes.getFieldType(type.toString())));
            setFieldValue(name); // if Field isn't correctly set, it will be set to null.
            return getFormField(name);
        } else {
            throw new FieldTypeNotFoundException("Field type you specified does not exist.");
        }
    }

    /**
     * Returns the value of a field or thows an exceptions.
     * @param name of field.
     * @return String containing the value of a Field.
     * @throws FieldNotFoundException
     */
    public String getFieldValue(String name) throws FieldNotFoundException {
        if ((getFormField(name)) != null) {
            if (getFormField(name).getFieldValue() != null) {
                return getFormField(name).getFieldValue();
            } else {
                return "Empty Field!";
            }
        }
        throw new FieldNotFoundException("The Parameter field you are looking for does not exist.");
    }

    /**
     * Set the value of a Field.
     * This method Uses the request.getParameterValues to retrieve the
     * desired parameter. If paramter does not exist FieldValue is set to null
     * If paramater is multi-valued an Array of FieldValues will be set.
     *
     * @param name of the Parameter we want to set.
     */
    private void setFieldValue(String name) {
        String[] fieldValues = request.getParameterValues(name);

        if (fieldValues != null) {
            // If only one value in Parameter
            if (fieldValues.length == 1) {
                getFormField(name).setFieldValue(fieldValues[0]);
            } else if (fieldValues.length > 1) { // If multi-valued
                // Convert Array[] String to ArrayList<String>
                ArrayList tmp_lst = new ArrayList<String>(Arrays.asList(fieldValues));
                getFormField(name).setFieldValues(tmp_lst);
            }
        } /*else {
            getFormField(name).setFieldValue(null); // Set to null

            // BUGGY should fix this.
            //getFormField(name).setFieldValues(null); // Set to null
        }*/
    }

    /**
     * Return a formfield in the Form. 
     * @param name
     * @return
     */
    private FormField getFormField(String name) {
        for (FormField formField : getFieldsList()) {
            if (formField.getName().toLowerCase().equals(name.toLowerCase())) {
                return formField;
            }
        }
        return null;
    }

    /**
     * Add a Custom Form Error to the Error ArrayList()
     * @return
     */
    public ArrayList<FormField> addError(String text) throws FieldTypeNotFoundException {
        try {
            FormField ff = new FormField("Custom Error", CUSTOM);
            ff.setFieldValue(text);
            errors.add(ff);
            return errors;
        } catch (Exception e) {
            throw new FieldTypeNotFoundException("Field type not found");
        }
    }
    // Getters & Setters.

    public HttpServletRequest getRequest() {
        return request;
    }

    public boolean getIsBound() {
        return isBound;
    }

    public ArrayList<FormField> getFieldsList() {
        return fieldsList;
    }

    public ArrayList<FormField> getErrors() {
        return errors;
    }
}
